package com.dkts.oil.test;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.math3.ml.clustering.DBSCANClusterer;
import org.apache.commons.math3.ml.clustering.Cluster;
import org.apache.commons.math3.ml.clustering.DoublePoint;

import java.io.*;
import java.util.*;

/**
 *  * 找出同编队的船，
 *  * 提取典型轨迹模型，通常指从同一编队的轨迹中提取出一个代表性轨迹，用于表示该编队的典型行为。这个典型轨迹模型通常需要通过以下步骤来实现：
 *  * 聚类与编队分析：首先需要对轨迹数据进行聚类分析，找出相似的轨迹组（编队）。
 *  * 轨迹归一化：对于同一编队内的轨迹，可能存在不同的起始点、时间或尺度。为了找到典型轨迹，需要对轨迹进行归一化处理。
 *  * 轨迹对齐：由于同一编队内的轨迹可能存在时间上的差异，需要对轨迹进行对齐。对齐通常依赖于动态时间规整（DTW）算法。
 *  * 提取代表性轨迹：根据聚类结果，提取每个编队的典型轨迹。这可以通过计算所有轨迹的平均轨迹，或选择最能代表编队行为的轨迹来实现。
 *  * 提取典型轨迹模型的代码示例
 *  * 下面的代码提供了一个简单的框架，来从轨迹数据中提取出每个编队的典型轨迹。
 *  * 主要思路：
 *  * 对编队内的轨迹进行聚类。
 *  * 对每个编队内的轨迹进行对齐（可使用DTW或其他对齐算法）。
 *  * 提取编队的代表性轨迹（比如平均轨迹、最优轨迹等）
 */
public class TypicalTrajectoryExtractor {

    // 设定最大距离阈值和最大时间间隔
    private static final double DISTANCE_THRESHOLD = 30000; // 30公里
    private static final long TIME_THRESHOLD = 40 * 60 * 1000; // 40分钟

    public static void main(String[] args) throws Exception {
        File csvFile = new File("C:\\Users\\heyou\\Documents\\WeChat Files\\wxid_86da35bc9zvi21\\FileStorage\\File\\2025-05\\c9a6717b7d2f5293cf7cd2ed05a0494c_2e2437ef9cd58c0d39af05569deeb4be_8.csv");
        Reader reader = new FileReader(csvFile);

        CSVParser parser = CSVFormat.DEFAULT
                .withFirstRecordAsHeader()
                .parse(reader);

        // 解析 CSV
        Map<String, List<CSVRecord>> mmsiMap = new HashMap<>();
        for (CSVRecord record : parser) {
            String mmsi = record.get("mmsi");
            if (!mmsiMap.containsKey(mmsi)) {
                mmsiMap.put(mmsi, new ArrayList<>());
            }
            mmsiMap.get(mmsi).add(record);
        }

        // DBSCAN 聚类
        List<DoublePoint> points = new ArrayList<>();
        Map<String, List<CSVRecord>> clusteredRecords = new HashMap<>();

        // 将轨迹数据转换为 DBSCAN 可用的 DoublePoint 格式
        for (Map.Entry<String, List<CSVRecord>> entry : mmsiMap.entrySet()) {
            List<CSVRecord> records = entry.getValue();
            for (CSVRecord record : records) {
                double lat = Double.parseDouble(record.get("lat"));
                double lon = Double.parseDouble(record.get("lon"));
                // 用 lat, lon 创建 DoublePoint
                points.add(new DoublePoint(new double[]{lat, lon}));
            }
        }

        // 执行 DBSCAN 聚类
        DBSCANClusterer<DoublePoint> clusterer = new DBSCANClusterer<>(DISTANCE_THRESHOLD, 10);
        List<Cluster<DoublePoint>> clusters = clusterer.cluster(points);

        // 根据聚类结果，提取每个编队的典型轨迹
        for (Cluster<DoublePoint> cluster : clusters) {
            // 对每个编队提取典型轨迹
            List<double[]> typicalTrajectory = extractTypicalTrajectory(cluster.getPoints());

            // 将典型轨迹输出为文件
            File folder = new File("fleet_" + cluster.getPoints().size());
            if (!folder.exists()) {
                folder.mkdir();
            }

            FileWriter writer = new FileWriter(folder.getPath() + "\\typical_trajectory.geojson");
            writer.write(createGeoJson(typicalTrajectory));
            writer.close();
            System.out.println("典型轨迹输出至：" + folder.getPath() + "\\typical_trajectory.geojson");
        }
    }

    // 提取典型轨迹：目前通过计算轨迹的平均位置来生成典型轨迹
    private static List<double[]> extractTypicalTrajectory(List<DoublePoint> points) {
        List<double[]> typicalTrajectory = new ArrayList<>();

        // 计算所有点的平均轨迹
        int numPoints = points.size();
        double sumLat = 0;
        double sumLon = 0;

        for (DoublePoint point : points) {
            sumLat += point.getPoint()[0];
            sumLon += point.getPoint()[1];
        }

        double avgLat = sumLat / numPoints;
        double avgLon = sumLon / numPoints;

        typicalTrajectory.add(new double[]{avgLat, avgLon});

        return typicalTrajectory;
    }

    // 创建GeoJSON字符串
    private static String createGeoJson(List<double[]> trajectory) {
        StringBuilder geoJson = new StringBuilder();
        geoJson.append("{\n");
        geoJson.append("\"type\": \"FeatureCollection\",\n");
        geoJson.append("\"features\": [\n");
        geoJson.append("{\n");
        geoJson.append("\"type\": \"Feature\",\n");
        geoJson.append("\"geometry\": {\n");
        geoJson.append("\"type\": \"LineString\",\n");
        geoJson.append("\"coordinates\": [\n");

        for (int i = 0; i < trajectory.size(); i++) {
            double[] point = trajectory.get(i);
            geoJson.append("[" + point[1] + ", " + point[0] + "]");
            if (i < trajectory.size() - 1) {
                geoJson.append(",\n");
            }
        }

        geoJson.append("\n]\n");
        geoJson.append("},\n");
        geoJson.append("\"properties\": {\n");
        geoJson.append("\"typical\": \"true\"\n");
        geoJson.append("}\n");
        geoJson.append("}\n");
        geoJson.append("]\n");
        geoJson.append("}");

        return geoJson.toString();
    }
}
